import click
import os
import stat
import tempfile

from subprocess import call, PIPE
from sys import exit, stdout, stderr

from compiledb import generate
from compiledb.utils import popen, cmd_join


class AutoconfMockScript:
    """This hack is intended to make it possible/feasible
    to use "make --dry-run --always-make" to speed up the compilation
    commands extraction with autoconf/automake based build systems.
    Without this ugly hack, particularly `--always-make` causes the
    autoconf's configure script to be executed to every make target,
    severely slowing down the process.
    To work around this issue we use a mock/empty script to be used
    in place of config.status and missing is they exist.
    """
    def __init__(self, verbose):
        self.verbose = verbose
        self.path = None
        self.mock_script = """#!/bin/bash
            ## Auto generated by compiledb. Do not modify!
            ## https://github.com/nickdiego/compiledb
            case "$*" in
                ./config.status*|*/missing*)
                    #echo "Skipping $@" >>/tmp/compiledb.log
                    exit 0
                    ;;
                *)
                    shift && test $# -gt 0 && /bin/sh -c "$@"
                    exit $?
                    ;;
            esac
            """

    def __enter__(self):
        if not os.path.isfile("config.status"):
            return self
        try:
            fd, tmp = tempfile.mkstemp()
            with os.fdopen(fd, 'w') as out:
                out.write(self.mock_script)
            os.chmod(tmp, stat.S_IEXEC | stat.S_IREAD)
            self.path = tmp
            return self
        except Exception as e:
            self.cleanup()
            raise e

    def __exit__(self, exc_type, exc_value, traceback):
        self.cleanup()

    def cleanup(self):
        if not self.path:
            return
        if self.verbose:
            print("Cleaning up autoconf mock resources..")
        if os.path.isfile(self.path):
            os.remove(self.path)


@click.command(name='make', context_settings=dict(ignore_unknown_options=True))
@click.option('-c', '--cmd', 'make_cmd', nargs=1, required=False,
              help="Command to be used as make executable.")
@click.argument('make_args', nargs=-1, type=click.UNPROCESSED)
@click.pass_context
def command(ctx, make_cmd, make_args):
    """Generates compilation database file for an arbitrary GNU Make command.
     Acts like a make wrapper, forwarding all MAKE_ARGS to make command"""
    make_cmd = make_cmd or 'make'
    logging_mode_flags = "-Bnkw"

    options = ctx.obj

    if not options.no_build:
        cmd = [make_cmd] + list(make_args)
        print("## Building [{}]...".format(' '.join(cmd)))
        ret = call(cmd, stdout=stdout, stderr=stderr)
        print()
        if ret != 0:
            exit(1)

    done = False
    args = vars(options)
    del args['no_build']
    with AutoconfMockScript(options.verbose) as mock_script:
        cmd = [make_cmd, logging_mode_flags] + list(make_args)
        if mock_script.path:
            cmd.append("SHELL={}".format(mock_script.path))
        pipe = popen(cmd_join(cmd), stdout=PIPE)
        options.infile = pipe.stdout
        del args['verbose']
        done = generate(**args)
        pipe.wait()
    exit(0 if done else 1)
